using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Reflection;

namespace Camalon.Drawing
{
	

	[Serializable]
	[TypeConverter(typeof(WarpConverter))]
	public struct Warp
	{
		private Point _topLeft;
		private Point _topRight;
		private Point _bottomLeft;
		private Point _bottomRight;
		private WarpMode _warpMode;

		public Warp( Point topLeft, Point topRight, Point bottomLeft, Point bottomRight)
			:this( topLeft,topRight, bottomLeft, bottomRight, WarpMode.Bilinear){}
		
		public Warp( Point topLeft, Point topRight, Point bottomLeft, Point bottomRight, WarpMode warpMode)
		{
			_topLeft	= topLeft;
			_topRight	= topRight;
			_bottomLeft = bottomLeft;
			_bottomRight= bottomRight;
			_warpMode	= warpMode;

		}

		public Warp(Warp warp)
		{
			_topLeft	= warp._topLeft;
			_topRight	= warp._topRight;
			_bottomLeft = warp._bottomLeft;
			_bottomRight= warp._bottomRight;
			_warpMode	= warp._warpMode;
		}


		#region operatoren
		public override bool Equals(object obj)
		{
			if(!( obj is Warp)) return false;

			return  this == (Warp)obj;
		}

		public bool Equals(Warp value)
		{
			return  this == value;
		}

		public override int GetHashCode()
		{
			return 
				_topLeft.GetHashCode()^
				_topRight.GetHashCode()^
				_bottomLeft.GetHashCode()^
				_bottomRight.GetHashCode()^
				_warpMode.GetHashCode();
		}

		public static bool operator ==(	Warp value1,Warp value2	)
		{
			return 
				value1._topLeft		== value2._topLeft && 
				value1._topRight	== value2._topRight && 
				value1._bottomLeft	== value2._bottomLeft && 
				value1._bottomRight == value2._bottomRight && 
				value1._warpMode	== value2._warpMode;
		}

		public static bool operator !=(	Warp value1,Warp value2	)
		{
			return !(value1== value2);
		}
		#endregion operatoren



		[DefaultValue( typeof(Point),"0,0" )]
		[RefreshProperties( RefreshProperties.Repaint)]
		public Point TopLeft
		{
			get{ return _topLeft;  }
			set
			{ 
				if( value == _topLeft) return;
				_topLeft = value;
			}
		}

		[DefaultValue( typeof(Point),"0,0" )]
		[RefreshProperties( RefreshProperties.Repaint)]
		public Point TopRight
		{
			get{ return _topRight;  }
			set
			{ 
				if( value == _topRight) return;
				_topRight = value;
			}
		}

		[DefaultValue( typeof(Point),"0,0" )]
		[RefreshProperties( RefreshProperties.Repaint)]
		public Point BottomLeft
		{
			get{ return _bottomLeft;  }
			set
			{ 
				if( value == _bottomLeft) return;
				_bottomLeft = value;
			}
		}

		[DefaultValue( typeof(Point),"0,0" )]
		[RefreshProperties( RefreshProperties.Repaint)]
		public Point BottomRight
		{
			get{ return _bottomRight;  }
			set
			{ 
				if( value == _bottomRight) return;

				_bottomRight = value;
			}
		}

		[DefaultValue( WarpMode.Bilinear)]
		[RefreshProperties( RefreshProperties.Repaint)]
		public WarpMode WarpMode
		{
			get{ return _warpMode;}
			set
			{ 
				if( value == _warpMode) return;
				_warpMode = value;
			}
		}


		public void Copy( Warp warpDestination )
		{
			warpDestination.TopLeft		= _topLeft;
			warpDestination.TopRight		= _topRight;
			warpDestination.BottomLeft		= _bottomLeft;
			warpDestination.BottomRight	= _bottomRight;
			warpDestination.WarpMode		= _warpMode;
		}

		[Browsable(false)]
		public bool IsEmpty
		{
			get
			{
				Point empty = Point.Empty;
				return	
					_topLeft	== empty &&
					_topRight	== empty &&
					_bottomLeft == empty &&
					_bottomRight== empty;
			}
		}


		public static Warp Empty
		{
			get
			{
				return new Warp(Point.Empty,Point.Empty,Point.Empty,Point.Empty);
			}
		}


		public PointF[] GetPoints(RectangleF bounds) 
		{ 
			PointF tl = new PointF( bounds.Left, bounds.Top ); 
			tl.X += _topLeft.X;		
			tl.Y += _topLeft.Y; 

			PointF tr = new PointF( bounds.Right ,bounds.Top ); 
			tr.X += _topRight.X;	
			tr.Y += _topRight.Y; 

			PointF bl = new PointF( bounds.Left , bounds.Bottom  ); 
			bl.X += _bottomLeft.X;	
			bl.Y += _bottomLeft.Y; 

			PointF br = new PointF( bounds.Right, bounds.Bottom ); 
			br.X += _bottomRight.X;	
			br.Y += _bottomRight.Y; 
                
                        
			return new PointF[]{tl,tr,bl,br}; 
		}



		public static PointF[] GetPoints(RectangleF bounds, Warp warp) 
		{ 
			PointF tl = new PointF( bounds.Left, bounds.Top ); 
			tl.X += warp._topLeft.X;		
			tl.Y += warp._topLeft.Y; 

			PointF tr = new PointF( bounds.Right ,bounds.Top ); 
			tr.X += warp._topRight.X;	
			tr.Y += warp._topRight.Y; 

			PointF bl = new PointF( bounds.Left , bounds.Bottom  ); 
			bl.X += warp._bottomLeft.X;	
			bl.Y += warp._bottomLeft.Y; 

			PointF br = new PointF( bounds.Right, bounds.Bottom ); 
			br.X += warp._bottomRight.X;	
			br.Y += warp._bottomRight.Y; 
                
                        
			return new PointF[]{tl,tr,bl,br}; 
		}

		public Warp WarpToCenter( RectangleF newBounds, RectangleF oldBounds)
		{
			return WarpToCenter( newBounds, GetPoints( oldBounds));
		}

		public static Warp WarpToCenter( RectangleF newBounds, PointF[] warpedPoints )
		{
			Matrix m = new Matrix();
			PointF center = RectangleUtil.CenterPoint( newBounds );
			PointF wCenter = RectangleUtil.CenterPoint( warpedPoints); 
			float px = wCenter.X - center.X;
			float py = wCenter.Y - center.Y;

			m.Translate( -px, -py);
			m.TransformPoints( warpedPoints );
			return Rewarp( newBounds, warpedPoints );
		}

		public Warp Rewarp( RectangleF newBounds, RectangleF oldBounds )
		{
			PointF[] ptsOld = GetPoints( oldBounds );
			return Rewarp( newBounds, ptsOld );

		}
		
		public static Warp Rewarp( RectangleF newBounds, PointF[] warpedPoints )
		{
			PointF tl = new PointF( newBounds.Left, newBounds.Top ); 
			PointF wtl = warpedPoints[0];
			wtl.X -= tl.X;		
			wtl.Y -= tl.Y; 

			PointF tr = new PointF( newBounds.Right ,newBounds.Top ); 
			PointF wtr = warpedPoints[1];
			wtr.X -= tr.X;	
			wtr.Y -= tr.Y; 

			PointF bl = new PointF( newBounds.Left , newBounds.Bottom  ); 
			PointF wbl = warpedPoints[2];
			wbl.X -= bl.X;	
			wbl.Y -= bl.Y; 

			PointF br = new PointF( newBounds.Right, newBounds.Bottom ); 
			PointF wbr = warpedPoints[3];
			wbr.X -= br.X;	
			wbr.Y -= br.Y; 
                
                        
			return new Warp(
				Point.Round(wtl),
				Point.Round(wtr),
				Point.Round(wbl),
				Point.Round(wbr)); 
			
		}

		public static RectangleF GetBounds( RectangleF warpedBound, Warp warp )
		{
			PointF tl = new PointF( warpedBound.Left, warpedBound.Top ); 
			tl.X -= warp._topLeft.X;		
			tl.Y -= warp._topLeft.Y; 

			PointF tr = new PointF( warpedBound.Right ,warpedBound.Top ); 
			tr.X -= warp._topRight.X;		
			tr.Y -= warp._topRight.Y; 

			PointF bl = new PointF( warpedBound.Left , warpedBound.Bottom  ); 
			bl.X -= warp._bottomLeft.X;		
			bl.Y -= warp._bottomLeft.Y; 

			PointF br = new PointF( warpedBound.Right, warpedBound.Bottom ); 
			br.X -= warp._bottomRight.X;	
			br.Y -= warp._bottomRight.Y; 

			return new RectangleF( tl.X,tl.Y, tr.X-tl.X, bl.Y - tl.Y);
		}



	}



}
